44 Python内置函数
44.1 引言Python内置函数的设计哲学
理论背景:函数式编程与内置函数
Python的内置函数(Built-in Functions)体现了语言的实用性(Pragmatism)设计哲学。这些函数无需导入任何模块即可使用,它们构成了Python编程的基础工具箱。从计算机科学的角度,内置函数的设计遵循以下原则:
- 高层抽象: 隐藏底层实现细节,提供简洁接口
- 泛型设计: 同一函数可应用于多种数据类型
- 性能优化: 用C语言实现,比纯Python代码快10-100倍
- 一致性: 遵循统一的命名和调用约定
历史背景:内置函数的演进
Python的内置函数集合随着语言发展不断丰富: - Python 1.x (1991-2000): 约50个内置函数,强调基础操作 - Python 2.x (2000-2010): 增加到约70个,引入enumerate(), sorted()等 - Python 3.x (2008-至今): 约80个,增加all(), any(), ascii()等
数学分类:内置函数的功能划分
按功能可将内置函数分为以下类别:
| 类别 | 代表函数 | 数量 | 金融应用场景 |
|---|---|---|---|
| 数学运算 | abs(), round(), sum() |
8 | 收益计算,四舍五入 |
| 序列操作 | len(), max(), min(), sorted() |
12 | 价格排序,极值查找 |
| 迭代器 | map(), filter(), zip() |
7 | 数据转换,批量处理 |
| 类型转换 | int(), float(), str() |
15 | 数据清洗,类型转换 |
| 对象操作 | isinstance(), id(), hash() |
10 | 类型检查,内存管理 |
| 输入输出 | print(), input(), open() |
5 | 数据读取,结果输出 |
补充说明:为什么内置函数用C实现?
以sum()函数为例:
# 纯Python实现(慢)
def sum_python(iterable):
total = 0
for item in iterable:
total += item
return total
# C实现(快)
# 省去了Python字节码解释的开销
# 直接在C层面循环,速度提升10-50倍性能对比测试(求和100万个整数): - sum_python(): 约50ms - sum(): 约5ms - 性能提升: 10倍
44.2 常用内置函数
平台任务解答代码
以下代码与教学平台任务要求完全一致:
# 注:该代码块包含未完成的填空代码,需要在平台上完成
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
#任务一
stock = ["中信证券","华泰证券","国泰君安","招商证券","中国银河","广发证券","中信建投","国信证券","中金公司","申万宏源"]
print(len(stock)) #计算列表中元素的个数
print(list(enumerate(stock,start=1))) #创建带有索引并且以列表方式分输出
#任务二
profit = [205.39,130.36,98.85,87.69,78.84,78.63,70.47,64.27,61.64,54.75]
stock = ["中信证券","华泰证券","国泰君安","招商证券","中国银河","广发证券","中信建投","国信证券","中金公司","申万宏源"] # 定义列表stock
profit_total = sum(profit) #计算10家证券公司的净利润总和
profit_average = profit_total/len(stock) # 获取数据长度
print("2023年净利润排名前10位的证券公司净利润总和(亿元)",profit_total) # 输出2023年净利润排名前10位的证券公司净利润总和(
print("2023年净利润排名前10位的证券公司净利润平均数(亿元)",round(profit_average,4)) # 输出2023年净利润排名前10位的证券公司净利润平均数
#任务三
return_Q1 = [-0.0588,-0.0067,-0.0698,0.0239,-0.0059,-0.0672,-0.0739,-0.0193,-0.1543,0.0046]
return_max = max(return_Q1) #找出最大涨幅
return_min = min(return_Q1) #找出最小涨幅
print("2024年1季度股价的最大涨幅",return_max) # 输出2024年1季度股价的最大涨幅
print("2024年1季度股价的最小涨幅",return_min) # 输出2024年1季度股价的最小涨幅
price = [18.73,13.61,13.47,13.71,11.76,13.05,21.68,8.11,32.03,4.40] # 定义列表price
price_sorted = sorted(price) #将股价由小到大排序
print(price_sorted) # 输出价格数据
#任务四
stock = ["中信证券","华泰证券","国泰君安","招商证券","中国银河","广发证券","中信建投","国信证券","中金公司","申万宏源"]
# 定义列表code
code = ["600030","601688","601211","600999","601881","000776","601066","002736","601995","000166"]
profit = [205.39,130.36,98.85,87.69,78.84,78.63,70.47,64.27,61.64,54.75] # 定义列表profit
# 定义列表return_Q1
return_Q1 = [-0.0588,-0.0067,-0.0698,0.0239,-0.0059,-0.0672,-0.0739,-0.0193,-0.1543,0.0046]
price = [18.73,13.61,13.47,13.71,11.76,13.05,21.68,8.11,32.03,4.40] # 定义列表price
print(list(zip(stock,code,profit,return_Q1,price))) #将多个列表中对应的元素打包为一个元组并以列表方式输出
#任务五
fund = 1e7 #H公司的投资资金额
price_huatai = 13.76 #华泰证券股票
N = 100 # 设置期数/数量为100
share = N*int(fund/(price_huatai*N)) # 将数据转换为整数
print("H公司购买华泰证券的股票数量",share) # 输出H公司购买华泰证券的股票数量# 创建数值列表,用于演示各种内置函数
numbers = [1, 2, 3, 4, 5]
# 1. len(): 获取序列长度
# 返回序列中元素的个数
# 适用于字符串、列表、元组、字典等
print(f'长度: {len(numbers)}')
# 输出: 5
# 2. max(): 返回最大值
# 对数值序列,返回最大的数
# 对字符串序列,按Unicode编码比较
print(f'最大值: {max(numbers)}')
# 输出: 5
# 3. min(): 返回最小值
# 与max()类似,返回最小的元素
print(f'最小值: {min(numbers)}')
# 输出: 1
# 4. sum(): 计算序列和
# 从左到右对元素求和
# 注意:sum()只支持数值类型
print(f'求和: {sum(numbers)}')
# 输出: 15 (1+2+3+4+5)
# 5. sorted(): 返回排序后的新列表
# 原序列保持不变
# 默认升序,reverse=True可降序
print(f'排序: {sorted(numbers, reverse=True)}')
# 输出: [5, 4, 3, 2, 1]
# 6. abs(): 返回绝对值
# 对整数或浮点数取绝对值
# 对复数,返回模长
print(f'绝对值: {abs(-10)}')
# 输出: 10
# 7. round(): 四舍五入
# 语法: round(number, ndigits)
# ndigits指定小数位数,默认为0(取整)
print(f'四舍五入: {round(3.14159, 2)}')
# 输出: 3.14
# 8. pow(): 幂运算
# 等价于 x ** y,但支持模运算
# pow(x, y, mod) 等价于 (x ** y) % mod
print(f'幂运算: {pow(2, 3)}')
# 输出: 8代码深度解析:
len()的时间复杂度:
# Python对象内部维护长度信息 # 因此len()的时间复杂度是O(1) # 列表 lst = [1, 2, 3] # 内部结构: PyObject_VAR_HEAD包含ob_size字段 print(len(lst)) # 直接读取ob_size,无需遍历 # 字符串 s = "hello" # 同样直接读取长度信息 print(len(s)) # O(1) # 但对于生成器等迭代器,len()不适用 # 需要转换为列表或手动计数max()/min()的多键排序:
# 复杂结构的多键比较 portfolio = [ {'name': '茅台', 'price': 1850, 'pe': 45}, {'name': '五粮液', 'price': 220, 'pe': 32}, {'name': '招商银行', 'price': 45, 'pe': 8} ] # 找出PE最高的股票 # key参数指定比较依据 highest_pe = max(portfolio, key=lambda x: x['pe']) print(f'PE最高: {highest_pe["name"]}, PE={highest_pe["pe"]}') # 找出价格最低的股票 lowest_price = min(portfolio, key=lambda x: x['price']) print(f'价格最低: {lowest_price["name"]}, 价格={lowest_price["price"]}')sorted()的稳定性:
# Python的排序是稳定的(stable) # 即相等元素的相对顺序保持不变 # 先按价格排序 stocks = [ ('茅台', 1850, '沪'), ('五粮液', 220, '深'), ('平安', 45, '深'), ('招行', 45, '沪') ] # 按价格升序,价格相同时保持原顺序 sorted_by_price = sorted(stocks, key=lambda x: x[1]) print(sorted_by_price) # ('平安', 45, '深') 在 ('招行', 45, '沪') 之前金融应用:计算统计量:
# 收益率序列 returns = [0.05, -0.02, 0.03, 0.07, -0.01] # 平均收益率 avg = sum(returns) / len(returns) print(f'平均收益率: {avg:.2%}') # 最大收益和最大亏损 max_return = max(returns) min_return = min(returns) print(f'最大收益: {max_return:.2%}') print(f'最大亏损: {min_return:.2%}') # 极差(Range) range_ = max(returns) - min(returns) print(f'收益极差: {range_:.2%}')round()的银行家舍入:
# Python 3使用"银行家舍入"(Round Half to Even) # 当正好为0.5时,舍入到最近的偶数 print(round(2.5)) # 2 (最近的偶数) print(round(3.5)) # 4 (最近的偶数) # 这不同于传统的"四舍五入" # 原因是减少统计偏差 # 传统四舍五入需要使用decimal模块 from decimal import Decimal, ROUND_HALF_UP print(Decimal('2.5').quantize(Decimal('1'), rounding=ROUND_HALF_UP)) # 3
44.3 高级内置函数
# 1. map(): 应用函数到可迭代对象的每个元素
# 语法: map(function, iterable, ...)
# 返回一个迭代器,需要用list()转换为列表
prices = ['10.5', '20.3', '15.8']
# 将字符串列表转换为浮点数列表
# float()是内置函数,将字符串转换为浮点数
prices_float = list(map(float, prices))
print(f'转换为浮点数: {prices_float}')
# 输出: [10.5, 20.3, 15.8]
# 2. filter(): 过滤元素
# 语法: filter(function, iterable)
# function返回True时保留元素,False时过滤掉
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 使用lambda表达式定义过滤条件
# lambda x: x % 2 == 0 表示"偶数"
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(f'偶数: {evens}')
# 输出: [2, 4, 6, 8, 10]
# 3. zip(): 配对多个可迭代对象
# 将多个序列对应位置的元素组合成元组
names = ['张三', '李四', '王五']
ages = [25, 30, 35]
# zip()返回迭代器,每个元素是一个元组
pairs = list(zip(names, ages))
print(f'配对: {pairs}')
# 输出: [('张三', 25), ('李四', 30), ('王五', 35)]
# zip()的妙用:字典构造
# 直接将两个列表转换为字典
name_age_dict = dict(zip(names, ages))
print(f'字典: {name_age_dict}')
# 输出: {'张三': 25, '李四': 30, '王五': 35}
# 4. enumerate(): 同时获取索引和元素
# 语法: enumerate(iterable, start=0)
# 返回(索引, 元素)的迭代器
stocks = ['茅台', '五粮液', '招行']
for index, stock in enumerate(stocks, start=1):
print(f'{index}. {stock}')
# 输出:
# 1. 茅台
# 2. 五粮液
# 3. 招行代码深度解析:
map()的等价实现:
# map()的纯Python实现 def map_python(func, iterable): result = [] for item in iterable: result.append(func(item)) return result # 使用示例 numbers = [1, 2, 3, 4, 5] squared = list(map(lambda x: x**2, numbers)) print(squared) # [1, 4, 9, 16, 25] # 等价于列表推导式 squared = [x**2 for x in numbers] print(squared) # [1, 4, 9, 16, 25] # 列表推导式通常更Pythonic且更易读filter()的等价实现:
# filter()的纯Python实现 def filter_python(func, iterable): result = [] for item in iterable: if func(item): # 如果函数返回True result.append(item) return result # 使用示例 numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # 过滤出大于5的数 greater_than_5 = list(filter(lambda x: x > 5, numbers)) print(greater_than_5) # [6, 7, 8, 9, 10] # 等价于列表推导式 greater_than_5 = [x for x in numbers if x > 5] print(greater_than_5) # [6, 7, 8, 9, 10]zip()的配对规则:
# zip()以最短的序列为准 list1 = [1, 2, 3, 4, 5] list2 = ['a', 'b', 'c'] # 只配对前3个元素 zipped = list(zip(list1, list2)) print(zipped) # [(1, 'a'), (2, 'b'), (3, 'c')] # zip(*)的反向操作:解包 matrix = [(1, 'a'), (2, 'b'), (3, 'c')] numbers, letters = zip(*matrix) print(numbers) # (1, 2, 3) print(letters) # ('a', 'b', 'c')函数式编程应用:
# 组合使用map()、filter()、reduce() from functools import reduce # reduce在Python 3中移到functools # 场景:计算股票组合的加权收益率 stocks = [ {'name': '茅台', 'return': 0.05, 'weight': 0.4}, {'name': '五粮液', 'return': 0.03, 'weight': 0.3}, {'name': '招行', 'return': 0.02, 'weight': 0.3} ] # 1. 提取收益率 returns = list(map(lambda x: x['return'], stocks)) # 2. 提取权重 weights = list(map(lambda x: x['weight'], stocks)) # 3. 计算加权收益率 # reduce()对序列进行累积计算 weighted_return = sum(r * w for r, w in zip(returns, weights)) print(f'加权收益率: {weighted_return:.2%}')金融应用:数据清洗流水线:
# 原始数据:字符串格式的价格和数量 raw_data = [ ('10.5', '100'), ('20.3', '200'), ('15.8', '150') ] # 使用map()进行数据转换 prices = list(map(lambda x: float(x[0]), raw_data)) quantities = list(map(lambda x: int(x[1]), raw_data)) # 计算每笔交易金额 amounts = list(map(lambda p, q: p * q, prices, quantities)) print(f'交易金额: {amounts}') # [1050.0, 4060.0, 2370.0] # 过滤出金额大于2000的交易 large_trades = list(filter(lambda x: x > 2000, amounts)) print(f'大额交易: {large_trades}') # [4060.0, 2370.0]
44.4 金融应用实例投资组合分析
# 定义投资组合的日收益率序列
# 正值表示上涨,负值表示下跌
returns = [0.05, -0.02, 0.03, 0.07, -0.01]
# 1. 基础统计:平均收益率
# sum()计算总和,len()获取个数
avg_return = sum(returns) / len(returns)
print(f'平均收益率: {avg_return:.2%}')
# 输出: 平均收益率: 2.40%
# 2. 统计正收益天数
# 使用列表推导式过滤正收益
# len()统计数量
positive_returns = [r for r in returns if r > 0]
positive_count = len(positive_returns)
print(f'正收益次数: {positive_count}/{len(returns)}')
# 输出: 正收益次数: 3/5
# 3. 计算最大回撤
# 最大回撤是从峰值到谷底的最大跌幅
# 这是一个重要的风险指标
cum_return = 1.0 # 初始累计收益
max_drawdown = 0 # 最大回撤
peak = 1.0 # 历史峰值
# 遍历每日收益率
for r in returns:
# 更新累计收益
# cum_return *= (1 + r) 等价于 cum_return = cum_return * (1 + r)
cum_return *= (1 + r)
# 更新历史峰值
# 如果当前收益超过历史峰值,更新峰值
if cum_return > peak:
peak = cum_return
# 计算当前回撤
# 回撤 = (峰值 - 当前值) / 峰值
drawdown = (peak - cum_return) / peak
# 更新最大回撤
# max()函数返回两个值中的较大者
max_drawdown = max(max_drawdown, drawdown)
print(f'最大回撤: {max_drawdown:.2%}')
# 输出: 最大回撤: 2.00%
# 4. 计算夏普比率(简化版)
# 夏普比率衡量单位风险的超额收益
# Sharpe = (平均收益率 - 无风险利率) / 标准差
# 首先计算标准差
import math # 导入math模块用于sqrt()
# 计算方差
mean = sum(returns) / len(returns)
variance = sum((r - mean) ** 2 for r in returns) / len(returns)
# 标准差是方差的平方根
std_dev = math.sqrt(variance)
# 假设无风险利率为2%(年化)
risk_free_rate = 0.02
# 计算夏普比率(年化)
# 假设252个交易日
sharpe_ratio = (mean * 252 - risk_free_rate) / (std_dev * math.sqrt(252))
print(f'夏普比率: {sharpe_ratio:.2f}')
# 输出某个数值
# 5. 计算收益率的分位数
# 使用sorted()和索引计算
sorted_returns = sorted(returns)
n = len(sorted_returns)
# 中位数(50%分位数)
if n % 2 == 0:
median = (sorted_returns[n//2 - 1] + sorted_returns[n//2]) / 2
else:
median = sorted_returns[n//2]
print(f'中位数收益率: {median:.2%}')
# 25%分位数(下四分位数)
q25_index = int(n * 0.25)
q25 = sorted_returns[q25_index]
print(f'25%分位数: {q25:.2%}')
# 75%分位数(上四分位数)
q75_index = int(n * 0.75)
q75 = sorted_returns[q75_index]
print(f'75%分位数: {q75:.2%}')代码深度解析:
最大回撤的计算原理:
# 最大回撤是评估策略风险的关键指标 # 它回答了"从最高点到最低点,最多亏损多少?" # 可视化理解 # 假设累计收益曲线如下: # 1.0 -> 1.05 -> 1.029 -> 1.0599 -> 1.1341 -> 1.1228 # ↑峰值 ↓回撤2% ↑新峰值 ↑新峰值 ↓回撤 # 计算步骤: # 1. 初始峰值 = 1.0 # 2. 第1天: 1.0 * 1.05 = 1.05, 新峰值 = 1.05 # 3. 第2天: 1.05 * 0.98 = 1.029, 回撤 = (1.05-1.029)/1.05 ≈ 2% # 4. 第3天: 1.029 * 1.03 = 1.0599, 新峰值 = 1.0599 # 5. 第4天: 1.0599 * 1.07 = 1.1341, 新峰值 = 1.1341 # 6. 第5天: 1.1341 * 0.99 = 1.1228, 回撤 = (1.1341-1.1228)/1.1341 ≈ 1% # 最终最大回撤 = max(2%, 1%) = 2%标准差的数学含义:
# 标准差衡量数据的离散程度 # 在金融中,标准差代表波动率,是风险的度量 # 计算公式: # σ = sqrt(Σ(xi - μ)² / n) # 其中: # - xi: 第i个收益率 # - μ: 平均收益率 # - n: 样本数量 # 手动计算示例 returns = [0.05, -0.02, 0.03, 0.07, -0.01] mean = sum(returns) / len(returns) # 0.024 # 计算每个收益率与均值的偏差平方 squared_deviations = [(r - mean) ** 2 for r in returns] # 方差是偏差平方的平均 variance = sum(squared_deviations) / len(returns) # 标准差是方差的平方根 std_dev = variance ** 0.5 print(f'标准差(波动率): {std_dev:.4f}')分位数的应用:
# 分位数描述数据的分布特征 # 在投资组合中的应用 # 假设有100只股票的收益率 import random stock_returns = [random.uniform(-0.1, 0.2) for _ in range(100)] # 找出表现最好的10%股票 sorted_returns = sorted(stock_returns, reverse=True) top_10_percent = sorted_returns[:10] print(f'前10%股票的平均收益率: {sum(top_10_percent)/len(top_10_percent):.2%}') # 找出表现最差的10%股票 bottom_10_percent = sorted_returns[-10:] print(f'后10%股票的平均收益率: {sum(bottom_10_percent)/len(bottom_10_percent):.2%}')性能优化:使用NumPy替代内置函数:
# 对于大规模数值计算,NumPy比内置函数快得多 import numpy as np import time # 生成大规模数据 large_returns = [random.random() for _ in range(1000000)] # 方法1:使用内置函数 start = time.time() mean_python = sum(large_returns) / len(large_returns) std_python = (sum((x - mean_python) ** 2 for x in large_returns) / len(large_returns)) ** 0.5 print(f'Python方法: {time.time() - start:.4f}秒') # 方法2:使用NumPy start = time.time() returns_array = np.array(large_returns) mean_numpy = returns_array.mean() std_numpy = returns_array.std() print(f'NumPy方法: {time.time() - start:.4f}秒') # NumPy通常快10-100倍all()和any()函数的应用:
# all():所有元素为True时返回True # any():任一元素为True时返回True # 应用1:检查所有收益率是否为正 returns = [0.01, 0.02, 0.03, -0.01, 0.02] all_positive = all(r > 0 for r in returns) print(f'所有收益为正: {all_positive}') # False # 应用2:检查是否有任何收益率为负 any_negative = any(r < 0 for r in returns) print(f'存在负收益: {any_negative}') # True # 应用3:风险控制 # 检查是否所有持仓的权重之和为1 weights = [0.3, 0.4, 0.3] weights_sum = sum(weights) print(f'权重归一化: {abs(weights_sum - 1.0) < 1e-10}') # True
最佳实践总结:
优先使用内置函数而非自己实现:
- 内置函数用C实现,性能更好
- 代码更简洁,可读性更高
- 经过充分测试,可靠性更强
函数式编程 vs 列表推导式:
# map/filter:函数式风格 result = list(map(lambda x: x**2, numbers)) # 列表推导式:更Pythonic result = [x**2 for x in numbers] # 推荐:列表表导式更易读性能优化建议:
- 大规模数值计算 → 使用NumPy
- 复杂逻辑判断 → 使用列表推导式或生成器
- 需要延迟计算 → 使用迭代器而非列表
常见陷阱:
# 陷阱1:修改正在遍历的列表 lst = [1, 2, 3, 4, 5] for item in lst: if item % 2 == 0: lst.remove(item) # 危险!可能跳过元素 # 正确做法:创建新列表 lst = [1, 2, 3, 4, 5] lst = [x for x in lst if x % 2 != 0] # 陷阱2:在可变默认参数中使用mutable对象 def foo(lst=[]): # 危险! lst.append(1) return lst # 正确做法 def foo(lst=None): if lst is None: lst = [] lst.append(1) return lst